C.S.Burner 🪙 ━►🔥GIT Node
Node / reticulum_mirror / MeshChatX / files / meshchatx / src / frontend / components / messages / telemetry / TelemetryBatteryChart.vue
Displaying Raw • Download
meshchatx/src/frontend/components/messages/telemetry/TelemetryBatteryChart.vue aeceb30d67ef2ff9ebf411a73daaab18438c5113 (aeceb30d) Text, 10.15 KB
T8b949e<!-- SPDX-License-Identifier: 0BSD AND MIT -->
Tff7b72<templateTff7b72>
Tff7b72<div Te6edf3v-if=Ta5d6ff"Ta5d6ffspecTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffspace-y-2Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-start justify-between gap-2 border-b border-gray-200/70 pb-2 dark:border-zinc-700/70Ta5d6ff"Tff7b72>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex items-center gap-2 min-w-0Ta5d6ff"Tff7b72>
Tff7b72<MaterialDesignIcon Te6edf3icon-name=Ta5d6ff"Ta5d6ffbattery-highTa5d6ff" Te6edf3class=Ta5d6ff"Ta5d6ffsize-4 shrink-0 text-sky-600 dark:text-sky-400Ta5d6ff" Tff7b72/>
Tff7b72<span
Te6edf3class=Ta5d6ff"Ta5d6fftext-[11px] font-semibold uppercase tracking-wide text-gray-500 dark:text-zinc-400 truncateTa5d6ff"
Tff7b72>
{{ Te6edf3$t("messages.telemetry_battery_trend") }}
Tff7b72</span>
Tff7b72</div>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fftext-[11px] tabular-nums font-medium text-gray-600 dark:text-zinc-300 shrink-0Ta5d6ff"Tff7b72>
{{ Te6edf3$t("messages.telemetry_battery_range", { from: firstPct, to: lastPct }) }}
Tff7b72</span>
Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6ffflex gap-1.5Ta5d6ff"Tff7b72>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffflex flex-col justify-between text-[9px] font-medium text-gray-400 dark:text-zinc-500 pr-0.5 pt-0.5 pb-6 w-5 text-right tabular-nums select-noneTa5d6ff"
Tff7b72>
Tff7b72<spanTff7b72>100Tff7b72</span>
Tff7b72<spanTff7b72>75Tff7b72</span>
Tff7b72<spanTff7b72>50Tff7b72</span>
Tff7b72<spanTff7b72>25Tff7b72</span>
Tff7b72<spanTff7b72>0Tff7b72</span>
Tff7b72</div>
Tff7b72<div
Te6edf3ref=Ta5d6ff"Ta5d6ffchartWrapTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffrelative flex-1 min-w-0 cursor-crosshair touch-noneTa5d6ff"
@Te6edf3mouseleave=Ta5d6ff"Ta5d6ffonChartLeaveTa5d6ff"
@Te6edf3mousemove=Ta5d6ff"Ta5d6ffonChartMoveTa5d6ff"
@Te6edf3touchstart=Ta5d6ff"Ta5d6ffonChartTouchTa5d6ff"
@touchmove.Te6edf3prevent=Ta5d6ff"Ta5d6ffonChartTouchTa5d6ff"
@Te6edf3touchend=Ta5d6ff"Ta5d6ffonChartLeaveTa5d6ff"
Tff7b72>
Tff7b72<svg
Te6edf3ref=Ta5d6ff"Ta5d6ffchartSvgTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffblock h-44 w-full text-sky-600 dark:text-sky-400Ta5d6ff"
Te6edf3:viewBox=Ta5d6ff"Ta5d6ffspec.viewBoxTa5d6ff"
Te6edf3role=Ta5d6ff"Ta5d6ffimgTa5d6ff"
Te6edf3:aria-label=Ta5d6ff"Te6edf3$tTa5d6ff(Ta5d6ff'Ta5d6ffmessages.telemetry_battery_trendTa5d6ff'Ta5d6ff)Ta5d6ff"
Tff7b72>
Tff7b72<defsTff7b72>
Tff7b72<linearGradient Te6edf3:id=Ta5d6ff"Ta5d6ffspec.gradientIdTa5d6ff" Te6edf3x1=Ta5d6ff"Ta5d6ff0%Ta5d6ff" Te6edf3y1=Ta5d6ff"Ta5d6ff0%Ta5d6ff" Te6edf3x2=Ta5d6ff"Ta5d6ff0%Ta5d6ff" Te6edf3y2=Ta5d6ff"Ta5d6ff100%Ta5d6ff"Tff7b72>
Tff7b72<stop Te6edf3offset=Ta5d6ff"Ta5d6ff0%Ta5d6ff" Te6edf3stop-color=Ta5d6ff"Ta5d6ffrgb(14, 165, 233)Ta5d6ff" Te6edf3stop-opacity=Ta5d6ff"Ta5d6ff0.38Ta5d6ff" Tff7b72/>
Tff7b72<stop Te6edf3offset=Ta5d6ff"Ta5d6ff55%Ta5d6ff" Te6edf3stop-color=Ta5d6ff"Ta5d6ffrgb(56, 189, 248)Ta5d6ff" Te6edf3stop-opacity=Ta5d6ff"Ta5d6ff0.14Ta5d6ff" Tff7b72/>
Tff7b72<stop Te6edf3offset=Ta5d6ff"Ta5d6ff100%Ta5d6ff" Te6edf3stop-color=Ta5d6ff"Ta5d6ffrgb(125, 211, 252)Ta5d6ff" Te6edf3stop-opacity=Ta5d6ff"Ta5d6ff0.03Ta5d6ff" Tff7b72/>
Tff7b72</linearGradient>
Tff7b72<linearGradient Te6edf3:id=Ta5d6ff"Ta5d6ffspec.strokeGradientIdTa5d6ff" Te6edf3x1=Ta5d6ff"Ta5d6ff0%Ta5d6ff" Te6edf3y1=Ta5d6ff"Ta5d6ff0%Ta5d6ff" Te6edf3x2=Ta5d6ff"Ta5d6ff100%Ta5d6ff" Te6edf3y2=Ta5d6ff"Ta5d6ff0%Ta5d6ff"Tff7b72>
Tff7b72<stop Te6edf3offset=Ta5d6ff"Ta5d6ff0%Ta5d6ff" Te6edf3stop-color=Ta5d6ff"Ta5d6ffrgb(2, 132, 199)Ta5d6ff" Tff7b72/>
Tff7b72<stop Te6edf3offset=Ta5d6ff"Ta5d6ff100%Ta5d6ff" Te6edf3stop-color=Ta5d6ff"Ta5d6ffrgb(14, 165, 233)Ta5d6ff" Tff7b72/>
Tff7b72</linearGradient>
Tff7b72</defs>
Tff7b72<g Te6edf3class=Ta5d6ff"Ta5d6fftext-gray-200/90 dark:text-zinc-700/90Ta5d6ff" Te6edf3stroke=Ta5d6ff"Ta5d6ffcurrentColorTa5d6ff" Te6edf3stroke-width=Ta5d6ff"Ta5d6ff0.5Ta5d6ff"Tff7b72>
Tff7b72<line
Te6edf3v-for=Ta5d6ff"Ta5d6ff(g, idx) in spec.gridLinesTa5d6ff"
Te6edf3:key=Ta5d6ff"Ta5d6ffidxTa5d6ff"
Te6edf3:x1=Ta5d6ff"Ta5d6ff0Ta5d6ff"
Te6edf3:x2=Ta5d6ff"Ta5d6ff100Ta5d6ff"
Te6edf3:y1=Ta5d6ff"Ta5d6ffg.y1Ta5d6ff"
Te6edf3:y2=Ta5d6ff"Ta5d6ffg.y2Ta5d6ff"
Te6edf3vector-effect=Ta5d6ff"Ta5d6ffnon-scaling-strokeTa5d6ff"
Tff7b72/>
Tff7b72</g>
Tff7b72<path Te6edf3:d=Ta5d6ff"Ta5d6ffspec.areaPathTa5d6ff" Te6edf3:fill=Ta5d6ff"Ta5d6ff`url(#Tff7b72${Te6edf3specTff7b72.Td2a8ffgradientIdTff7b72}Ta5d6ff)`Ta5d6ff" Te6edf3stroke=Ta5d6ff"Ta5d6ffnoneTa5d6ff" Tff7b72/>
Tff7b72<path
Te6edf3:d=Ta5d6ff"Ta5d6ffspec.linePathTa5d6ff"
Te6edf3fill=Ta5d6ff"Ta5d6ffnoneTa5d6ff"
Te6edf3:stroke=Ta5d6ff"Ta5d6ff`url(#Tff7b72${Te6edf3specTff7b72.Td2a8ffstrokeGradientIdTff7b72}Ta5d6ff)`Ta5d6ff"
Te6edf3stroke-width=Ta5d6ff"Ta5d6ff1.25Ta5d6ff"
Te6edf3stroke-linecap=Ta5d6ff"Ta5d6ffroundTa5d6ff"
Te6edf3stroke-linejoin=Ta5d6ff"Ta5d6ffroundTa5d6ff"
Te6edf3vector-effect=Ta5d6ff"Ta5d6ffnon-scaling-strokeTa5d6ff"
Tff7b72/>
Tff7b72<circle
Te6edf3:cx=Ta5d6ff"Ta5d6ffspec.first.xTa5d6ff"
Te6edf3:cy=Ta5d6ff"Ta5d6ffspec.first.yTa5d6ff"
Te6edf3r=Ta5d6ff"Ta5d6ff1.5Ta5d6ff"
Te6edf3fill=Ta5d6ff"Ta5d6ffwhiteTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffdark:fill-zinc-900Ta5d6ff"
Te6edf3stroke=Ta5d6ff"Ta5d6ffrgb(2, 132, 199)Ta5d6ff"
Te6edf3stroke-width=Ta5d6ff"Ta5d6ff1Ta5d6ff"
Te6edf3vector-effect=Ta5d6ff"Ta5d6ffnon-scaling-strokeTa5d6ff"
Tff7b72/>
Tff7b72<circle
Te6edf3:cx=Ta5d6ff"Ta5d6ffspec.last.xTa5d6ff"
Te6edf3:cy=Ta5d6ff"Ta5d6ffspec.last.yTa5d6ff"
Te6edf3r=Ta5d6ff"Ta5d6ff2Ta5d6ff"
Te6edf3fill=Ta5d6ff"Ta5d6ffrgb(14, 165, 233)Ta5d6ff"
Te6edf3stroke=Ta5d6ff"Ta5d6ffwhiteTa5d6ff"
Te6edf3stroke-width=Ta5d6ff"Ta5d6ff0.85Ta5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffdark:stroke-zinc-900Ta5d6ff"
Te6edf3vector-effect=Ta5d6ff"Ta5d6ffnon-scaling-strokeTa5d6ff"
Tff7b72/>
Tff7b72<g Te6edf3v-if=Ta5d6ff"Ta5d6ffhoverTa5d6ff"Tff7b72>
Tff7b72<line
Te6edf3:x1=Ta5d6ff"Ta5d6ffhover.gxTa5d6ff"
Te6edf3:x2=Ta5d6ff"Ta5d6ffhover.gxTa5d6ff"
Te6edf3:y1=Ta5d6ff"Ta5d6ffspec.layout.PTTa5d6ff"
Te6edf3:y2=Ta5d6ff"Ta5d6ffspec.plotBottomTa5d6ff"
Te6edf3stroke=Ta5d6ff"Ta5d6ffcurrentColorTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6fftext-sky-600/70 dark:text-sky-400/80Ta5d6ff"
Te6edf3stroke-width=Ta5d6ff"Ta5d6ff0.65Ta5d6ff"
Te6edf3stroke-dasharray=Ta5d6ff"Ta5d6ff3 2.5Ta5d6ff"
Te6edf3vector-effect=Ta5d6ff"Ta5d6ffnon-scaling-strokeTa5d6ff"
Te6edf3stroke-linecap=Ta5d6ff"Ta5d6ffroundTa5d6ff"
Tff7b72/>
Tff7b72<circle
Te6edf3:cx=Ta5d6ff"Ta5d6ffhover.gxTa5d6ff"
Te6edf3:cy=Ta5d6ff"Ta5d6ffhover.gyTa5d6ff"
Te6edf3r=Ta5d6ff"Ta5d6ff2Ta5d6ff"
Te6edf3fill=Ta5d6ff"Ta5d6ffwhiteTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffdark:fill-zinc-900Ta5d6ff"
Te6edf3stroke=Ta5d6ff"Ta5d6ffrgb(2, 132, 199)Ta5d6ff"
Te6edf3stroke-width=Ta5d6ff"Ta5d6ff1Ta5d6ff"
Te6edf3vector-effect=Ta5d6ff"Ta5d6ffnon-scaling-strokeTa5d6ff"
Tff7b72/>
Tff7b72</g>
Tff7b72</svg>
Tff7b72<div
Te6edf3v-if=Ta5d6ff"Ta5d6ffhoverTa5d6ff"
Te6edf3class=Ta5d6ff"Ta5d6ffpointer-events-none absolute z-10 rounded-md border border-sky-200/90 bg-white/95 px-2 py-1 text-[10px] font-semibold tabular-nums text-sky-900 shadow-md dark:border-sky-800 dark:bg-zinc-900/95 dark:text-sky-100Ta5d6ff"
Te6edf3:style=Ta5d6ff"Ta5d6ffhoverTipStyleTa5d6ff"
Tff7b72>
Tff7b72<divTff7b72>{{ hover.pct }}%Tff7b72</div>
Tff7b72<div Te6edf3class=Ta5d6ff"Ta5d6fffont-normal text-gray-500 dark:text-zinc-400Ta5d6ff"Tff7b72>{{ hover.timeLabel }}Tff7b72</div>
Tff7b72</div>
Tff7b72<div
Te6edf3class=Ta5d6ff"Ta5d6ffpointer-events-none absolute inset-x-0 bottom-0 flex h-6 justify-between px-0.5 text-[9px] tabular-nums text-gray-400 dark:text-zinc-500Ta5d6ff"
Tff7b72>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fftruncate max-w-[45%]Ta5d6ff"Tff7b72>{{ startLabel }}Tff7b72</span>
Tff7b72<span Te6edf3class=Ta5d6ff"Ta5d6fftruncate max-w-[45%] text-rightTa5d6ff"Tff7b72>{{ endLabel }}Tff7b72</span>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</div>
Tff7b72</template>
Tff7b72<scriptTff7b72>
import MaterialDesignIcon from "../../MaterialDesignIcon.vue";
import {
buildTelemetryBatteryChartSpec,
clampBatteryPercent,
interpolateBatteryByTime,
} from "../../../js/telemetryBatteryChartSpec.js";
import dayjs from "dayjs";
export default {
name: "TelemetryBatteryChart",
components: { MaterialDesignIcon },
props: {
samples: {
type: Array,
required: true,
},
idSuffix: {
type: String,
default: "default",
},
},
data() {
return {
hover: null,
};
},
computed: {
spec() {
return buildTelemetryBatteryChartSpec(this.samples, this.idSuffix);
},
firstPct() {
if (!this.samples.length) return 0;
return Math.round(this.samples[0].y);
},
lastPct() {
if (!this.samples.length) return 0;
return Math.round(this.samples[this.samples.length - 1].y);
},
startLabel() {
if (!this.samples.length) return "";
return dayjs(this.samples[0].x * 1000).format("MMM D, HH:mm");
},
endLabel() {
if (!this.samples.length) return "";
return dayjs(this.samples[this.samples.length - 1].x * 1000).format("MMM D, HH:mm");
},
hoverTipStyle() {
if (!this.hover) return {};
return {
left: `${this.hover.tipLeft}px`,
top: `${this.hover.tipTop}px`,
transform: "translate(-50%, calc(-100% - 6px))",
};
},
},
methods: {
svgPointFromClient(clientX, clientY) {
const svg = this.$refs.chartSvg;
if (!svg) return null;
const ctm = svg.getScreenCTM();
if (!ctm) return null;
return new DOMPoint(clientX, clientY).matrixTransform(ctm.inverse());
},
updateHoverFromClient(clientX, clientY) {
const spec = this.spec;
if (!spec) return;
const p = this.svgPointFromClient(clientX, clientY);
if (!p) return;
const { PL, PR, PT, PB, minX, maxX } = spec.layout;
let gx = p.x;
if (gx Tff7b72< PL) Te6edf3gx = Ta5d6ffPL;
if (gx Tff7b72> PR) gx = PR;
const rangeX = maxX - minX || 1;
const ts = minX + ((gx - PL) / (PR - PL)) * rangeX;
const { y } = interpolateBatteryByTime(this.samples, ts);
const gy = PT + (1 - clampBatteryPercent(y) / 100) * (PB - PT);
const wrap = this.$refs.chartWrap;
let tipLeft = 0;
let tipTop = 0;
if (wrap) {
const r = wrap.getBoundingClientRect();
tipLeft = Math.min(Math.max(clientX - r.left, 10), Math.max(10, r.width - 10));
tipTop = clientY - r.top;
}
this.hover = {
gx,
gy,
pct: Math.round(y),
timeLabel: dayjs(ts * 1000).format("MMM D, HH:mm"),
tipLeft,
tipTop,
};
},
onChartMove(ev) {
this.updateHoverFromClient(ev.clientX, ev.clientY);
},
onChartTouch(ev) {
const t = ev.touches[0];
if (t) this.updateHoverFromClient(t.clientX, t.clientY);
},
onChartLeave() {
this.hover = null;
},
},
};
Tff7b72</script>
Served by rngit 1.5.2 - Generated in 0.03s